package com.snailhd.cloud.redis.service.impl;

import com.snailhd.cloud.redis.RedisHandler;
import com.snailhd.cloud.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.*;
import redis.clients.jedis.params.geo.GeoRadiusParam;
import redis.clients.util.Pool;

import java.util.*;

/**
 * @ClassName RedisServiceImpl
 * @Description
 * @Author hd
 * @Date 2019-06-09 11:23
 */

@Slf4j
@Service
public class RedisServiceImpl implements RedisService {

    protected static final String SET_IF_NOT_EXIST = "NX";

    private static final String SET_WITH_EXPIRE_TIME = "EX";

    private static final String LOCK_SUCCESS = "OK";

    private static final Long RELEASE_SUCCESS = 1L;

    @Autowired
    private Pool<Jedis> jedisPool;


    public <T> T exec(RedisHandler handler){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return handler.handle(jedis);
        } catch (Exception e) {
            log.error("redis 执行异常：",e);
            return null;
        } finally {
            if (null != jedis) {
                jedis.close();
            }
        }
    }


    @Override
    public String get(String key) {
        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }

    @Override
    public String set(String key, String val) {

        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.set(key,val);
            }
        });
    }

    @Override
    public Set<String> keys(String pattern) {
        return exec(new RedisHandler() {
            @Override
            public Set<String> handle(Jedis jedis) {
                return jedis.keys(pattern);
            }
        });
    }

    @Override
    public String setex(String key, int seconds, String value) {
        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.setex(key,seconds,value);
            }
        });
    }

    @Override
    public Long expire(String key, int seconds) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.expire(key,seconds);
            }
        });
    }

    @Override
    public List<String> mget(String... keys) {

        return exec(new RedisHandler() {
            @Override
            public List<String> handle(Jedis jedis) {
                return jedis.mget(keys);
            }
        });
    }

    @Override
    public String hget(String key, String field) {

        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.hget(key,field);
            }
        });
    }

    @Override
    public Map<String, String> hgetAll(String key) {
        return exec(new RedisHandler() {
            @Override
            public Map<String, String> handle(Jedis jedis) {
                return jedis.hgetAll(key);
            }
        });
    }

    @Override
    public Long hset(String key, String field, String value) {

        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.hset(key,field,value);
            }
        });
    }

    @Override
    public String hmset(String key, Map<String, String> map) {
        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.hmset(key,map);
            }
        });
    }

    @Override
    public List<String> hmget(String key, String... fields){

        return exec(new RedisHandler() {
            @Override
            public List<String> handle(Jedis jedis) {
                return jedis.hmget(key,fields);
            }
        });
    }

    @Override
    public Long del(String... keys) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.del(keys);
            }
        });
    }

    @Override
    public Long hdel(String key, String... fields) {

        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.hdel(key,fields);
            }
        });
    }

    @Override
    public Long lpush(String key, String... values) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.lpush(key,values);
            }
        });
    }

    @Override
    public List<String> lrange(String key, Long start, Long end) {
        return exec(new RedisHandler() {
            @Override
            public List<String> handle(Jedis jedis) {
                Long startTemp = start, endTemp = end;
                if (null == startTemp) {
                    startTemp = 0L;
                }
                if (null == endTemp) {
                    endTemp = -1L;
                }
                return jedis.lrange(key,startTemp,endTemp);
            }
        });
    }

    @Override
    public List<String> scan(int cursor, String pattern, Integer count) {

        return exec(new RedisHandler() {
            @Override
            public List<String> handle(Jedis jedis) {
                ScanParams scanParams = new ScanParams();
                int defaultCount = 100;
                boolean queryAll = false;
                if (null != count) {
                    defaultCount = count;
                } else {
                    queryAll = true;
                }
                scanParams.count(defaultCount);
                scanParams.match(pattern);
                ScanResult<String> result;
                result = jedis.scan(String.valueOf(cursor), scanParams);
                if (!queryAll) {
                    return result.getResult();
                }
                // 模拟查询所有的数据
                List<String> all = new ArrayList<String>();
                String i = result.getStringCursor();
                all.addAll(result.getResult());
                while (!"0".equals(i)) {
                    result = jedis.scan(i, scanParams);
                    i = result.getStringCursor();
                    all.addAll(result.getResult());
                }
                return all;
            }
        });
    }

    @Override
    public String mset(Map<String, String> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }
        int len = map.size() * 2;
        String[] keysvalues = new String[len];
        List<String> keyvalueList = new ArrayList<String>(len);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            keyvalueList.add(entry.getKey());
            keyvalueList.add(entry.getValue());
        }
        return mset(keyvalueList.toArray(keysvalues));
    }

    @Override
    public String mset(String... keysvalues) {
        return exec(new RedisHandler() {
            @Override
            public String handle(Jedis jedis) {
                return jedis.mset(keysvalues);
            }
        });
    }

    @Override
    public Long msetnx(Map<String, String> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }
        int len = map.size() * 2;
        String[] keysvalues = new String[len];
        List<String> keyvalueList = new ArrayList<String>(len);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            keyvalueList.add(entry.getKey());
            keyvalueList.add(entry.getValue());
        }
        return msetnx(keyvalueList.toArray(keysvalues));
    }

    @Override
    public Long msetnx(String... keysvalues) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.msetnx(keysvalues);
            }
        });
    }

    @Override
    public Long zrem(String key, String member) {

        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.zrem(key, member);
            }
        });
    }

    @Override
    public Long incr(String key) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.incr(key);
            }
        });
    }

    @Override
    public Long lrem(String key, int count, String value) {

        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.lrem(key, count, value);
            }
        });
    }

    @Override
    public Long hincrBy(String key, String field, Long value) {
        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.hincrBy(key, field, value);
            }
        });
    }

    @Override
    public Long geoadd(String key, double longitude, double latitude, String member) {

        return exec(new RedisHandler() {
            @Override
            public Long handle(Jedis jedis) {
                return jedis.geoadd(key, longitude, latitude, member);
            }
        });
    }

    @Override
    public List<GeoRadiusResponse> georadius(String key, double longitude, double latitude, double radius, int num) {
        return exec(new RedisHandler() {
            @Override
            public List<GeoRadiusResponse> handle(Jedis jedis) {
                return jedis.georadius(key, longitude, latitude, radius, GeoUnit.M,
                        GeoRadiusParam.geoRadiusParam().withCoord().withDist().sortAscending().count(num));
            }
        });
    }

    @Override
    public Boolean distributedLock(String lockKey, String requestId, long expireTime) {
        return exec(new RedisHandler() {
            @Override
            public Boolean handle(Jedis jedis) {
                String ret = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
                return LOCK_SUCCESS.equals(ret);
            }
        });
    }

    @Override
    public Boolean releaseDistributedLock(String lockKey, String requestId) {
        return exec(new RedisHandler() {
            @Override
            public Boolean handle(Jedis jedis) {
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                Object obj = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
                return RELEASE_SUCCESS.equals(obj);
            }
        });
    }

}
